from yibao_orm.Field import Field


class ModelMetaClass(type):
    def __new__(cls, name, bases, attrs):
        if name == 'Model':
            return type.__new__(cls, name, bases, attrs)

        if 'id' not in attrs:
            raise TypeError('模型中必须要有id字段')
        pass_field = []  # 过滤掉字段
        mappings = {}  # 收集所有field对象 TODO 有序字典
        col_def = []  # 建立表时候 字段名称和类型  eg: [username varchar(100), ...]
        for key, value in attrs.items():
            if isinstance(value, Field):
                if value.verbose_name in pass_field:
                    continue
                mappings[key] = value
                col_def.append(str(value.verbose_name) + " " + str(value.column_type))

        attrs['__col_def__'] = col_def
        attrs['__mappings__'] = mappings
        attrs['__table__'] = name.lower()
        return type.__new__(cls, name, bases, attrs)


def check_table_existed(conn, table_name):
    """检测table是否存在数据库中"""
    sql = 'SELECT * FROM sqlite_master WHERE type = ? AND name = ? '
    result = conn.execute(sql, ('table', table_name))
    return len(result.fetchall()) == 1


def check_or_create_table(cls):
    """检测table是否存在，不存在则创建table"""
    if not check_table_existed(cls.db, cls.__table__):
        col = ','.join(cls.__col_def__)
        sql = f'CREATE TABLE {cls.__table__} ({col}) '
        cls.db.execute(sql)


def create_queryset(cls, fetc_all):
    """
    创建查询结果集
    """

    class AutoClass:
        def delete(self):
            sql_delete = 'DELETE FROM {} WHERE id = {}'
            self.db.execute(sql_delete.format(self.__table__, self.id))
            self.db.commit()

        def update(self, **kwargs):
            params = []
            sql_update = 'UPDATE {} SET {} WHERE id = {}'
            sql_k_v = '{} = "{}"'
            for k, v in kwargs.items():
                col_name = self.__mappings__.get(k).verbose_name
                params.append(sql_k_v.format(col_name, v))
            sql = sql_update.format(self.__table__, ','.join(params), self.id)
            self.db.execute(sql)
            self.db.commit()

        def __str__(self):
            return f'<id:{self.id}>'

        __repr__ = __str__

    query_set = []
    key_list = list(cls.__mappings__.keys())  # 将模型类所有field字段组成一个列表

    for row in fetc_all:
        obj = AutoClass()
        for col, field in zip(row, key_list):
            obj.__setattr__(field, col)
        obj.__setattr__('__table__', cls.__table__)
        obj.__setattr__('db', cls.db)
        obj.__setattr__('__mappings__', cls.__mappings__)
        query_set.append(obj)

    return query_set


class Model(dict, metaclass=ModelMetaClass):
    db = None

    def __init__(self, **kwargs):
        super(Model, self).__init__(**kwargs)

    def save(self):
        fields = []  # 字段名称
        params = []  # 值
        sql_int = '{}'
        sql_var = '"{}"'
        sql_insert = 'INSERT INTO {} ({}) VALUES ({})'
        for k, v in self.__mappings__.items():
            if k in self:
                fields.append(v.verbose_name)
                if isinstance(self[k], int):
                    params.append(sql_int.format(self[k]))
                else:
                    params.append(sql_var.format(self[k]))
        sql = sql_insert.format(self.__table__, ",".join(fields), ",".join(params))
        check_or_create_table(self.__class__)  # 建表
        self.db.execute(sql)
        self.db.commit()

    def filter(self, **kwargs):
        params = []  # 值
        sql1 = 'SELECT * FROM {} WHERE {}'
        sql2 = 'SELECT * FROM {}'
        sql_k_v = '{} = "{}"'

        if kwargs.__len__() == 0:
            sql = sql2.format(self.__table__)
            fec = self.db.execute(sql)
            return create_queryset(self.__class__, fec.fetchall())

        for k, v in kwargs.items():
            if self.__mappings__.get(k):
                params.append(sql_k_v.format(self.__mappings__.get(k).verbose_name, v))
        sql = sql1.format(self.__table__, ' AND '.join(params))
        fec = self.db.execute(sql)
        return create_queryset(self.__class__, fec.fetchall())
